#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <signal.h>

#include "estructuras.h"
#include "constantes.h"
#include "sockets.h"
#include "configuracion.h"
#include "listas.h"
#include "NIPC.h"
#include "hiloDisco.h"


void * HiloDeDisco (void * pNodoDisco)
{

	char buffer[512];
	char buffer_sect[20];
	char buffer_id[20];
	/*int sectoresRecibidos=0;
	int sectoresEnviados=99;*/
	int yaesta=0;
	char *brkt;
	int contador;
	int pvez=1;
	disco* discoSinc=NULL;
	int sector_num;
	int socketSincro;
	int socketACerrar;
	
	disco * pDisco = (disco*) pNodoDisco;
	
	
	while(1)
	{
		
		InfoIPC rInfoIPC;
		if(recibirIPC(pDisco->iSocketHDAParaDA,&rInfoIPC)<0){
			if(estaEnVecSincro(pDisco->iSocketHDAParaDA)){	
				if(pDisco->iSocketHDAParaDA == VectorSincronizacion[atoi(buffer_id)].discoLect->iSocketHDAParaDA){
					VectorSincronizacion[atoi(buffer_id)].closeDiscoLect=1;
				}	
				if(pDisco->iSocketHDAParaDA == VectorSincronizacion[atoi(buffer_id)].discoSinc->iSocketHDAParaDA)
					VectorSincronizacion[atoi(buffer_id)].closeDiscoSinc=1;
			}
			
			if(VectorSincronizacion[atoi(buffer_id)].closeDiscoLect==1){
				socketACerrar=VectorSincronizacion[atoi(buffer_id)].discoSinc->iSocketHDAParaDA;
				eliminar(atoi(buffer_id));
				cerrarSocket(socketACerrar);	
			}
			
			free(rInfoIPC.payload);
			printf("%s se ha desconectado \n",pDisco->sNombre);
			reallocateoperations(pDisco);
			return NULL;
		}
		
		if(rInfoIPC.payLoadDescriptor==FINSINCRO){
			printf("Se termino de sincronizar: %s \n",pDisco->sNombre);
			printf("Se le enviaran los pedidos de escritura pendientes\n");
			enviarPendientes(pDisco->sNombre);
		}		
		else
		{
		
			char *payload = (char *) malloc(rInfoIPC.payLoadLength);
			memcpy(payload, rInfoIPC.payload, rInfoIPC.payLoadLength);
			
			memset(buffer,'\0',512);
			memset(buffer_id,'\0',20);
			memset(buffer_sect,'\0',20);
			
			if(strchr(rInfoIPC.payload, ';') != NULL)
			{
				strcpy(buffer, strtok_r(rInfoIPC.payload, ";",&brkt));
				strcpy(buffer_id, strtok_r(buffer, ",",&brkt));
				strcpy(buffer_sect, strtok_r(NULL, "\r\n\0",&brkt));
			}
			else{
				free(payload);
				continue;
			}
			
			free(rInfoIPC.payload);
			
			sector_num = atoi(buffer_sect);		
		
		
			if((rInfoIPC.payLoadDescriptor == RTA_READ_SECTOR_REQUEST)||(rInfoIPC.payLoadDescriptor == RTA_WRITE_SECTOR_REQUEST)){
			
			
				if(pthread_mutex_lock(&mutex)!=0){
					perror("pthread_mutex_lock");
					exit(EXIT_FAILURE);
				}
				
				pedido *nodoaAtender = (pedido *)BuscarDescriptorEnListaDeDisco(colaGlobal, buffer_id);
			
				if(pthread_mutex_unlock(&mutex)!=0){
				perror("pthread_mutex_unlock");
				exit(EXIT_FAILURE);
				}
				
						
				if(rInfoIPC.payLoadDescriptor==RTA_READ_SECTOR_REQUEST){
				
					/*printf("Se recibio la confirmacion de lectura del sector %s por parte del disco %s \n",buffer_sect,pDisco->sNombre);*/
					
					/*Le envio al cliente FSS el numero de sector respondido*/
					InfoIPC rInfoIPC3;
					rInfoIPC3.payLoadLength= strlen(buffer_sect) + 1;
					generarStructIPC(buffer_sect ,rInfoIPC.payLoadDescriptor,&rInfoIPC3);
					enviarIPC(atoi(nodoaAtender->sSocketPorElCualResponder),&rInfoIPC3);
					free(rInfoIPC3.payload);
					
					/*Le envio al cliente FSS la data correspondiente*/
					InfoIPC rInfoIPC4;
					rInfoIPC4.payLoadLength=512;
					generarStructIPC(payload+strlen(buffer_id) + 1 + strlen(buffer_sect) + 1  ,rInfoIPC.payLoadDescriptor,&rInfoIPC4);
					enviarIPC(atoi(nodoaAtender->sSocketPorElCualResponder),&rInfoIPC4);				
					free(rInfoIPC4.payload);
					
					if(pthread_mutex_lock(&mutex)!=0){
						perror("pthread_mutex_lock");
						exit(EXIT_FAILURE);
					}
					
					SacarNodoDescriptoresDeLista2(&nodoaAtender,&(colaGlobal),&(colaGlobal_Ult));
				
					if(pthread_mutex_unlock(&mutex)!=0){
						perror("pthread_mutex_unlock");
						exit(EXIT_FAILURE);
					}
						
					free(nodoaAtender);	
					free(payload);
				}
				
				
				if(rInfoIPC.payLoadDescriptor==RTA_WRITE_SECTOR_REQUEST){
				
					/**************PEDIDO COMUN***************/

					if(pthread_mutex_lock(&mutex)!=0){
						perror("pthread_mutex_lock");
						exit(EXIT_FAILURE);
					}
					
					if(nodoaAtender->discosAEspejar>1)
					{
				
						/*ACTUALIZO DISCOS A ESPEJAR*/
						
						nodoaAtender->discosAEspejar--;
						
						if(pthread_mutex_unlock(&mutex)!=0){
							perror("pthread_mutex_unlock");
							exit(EXIT_FAILURE);
						}
						
						

					}
					else
					{					
						printf("Se recibio la confirmacion de escritura del sector %i por parte de todos los discos existentes \n",nodoaAtender->numeroSector);
						
						/*SACO DE COLA*/
					
						SacarNodoDescriptoresDeLista2(&nodoaAtender,&(colaGlobal),&(colaGlobal_Ult));
				
						if(pthread_mutex_unlock(&mutex)!=0){
							perror("pthread_mutex_unlock");
							exit(EXIT_FAILURE);
						}
						
						/*Le envio al cliente FSS el numero de sector respondido*/
						InfoIPC rInfoIPC3;
						rInfoIPC3.payLoadLength= strlen(buffer_sect) + 1;
						generarStructIPC(buffer_sect ,rInfoIPC.payLoadDescriptor,&rInfoIPC3);
						enviarIPC(atoi(nodoaAtender->sSocketPorElCualResponder),&rInfoIPC3);
						free(rInfoIPC3.payload);
						
						free(nodoaAtender);
					}
				
					free(payload);
				}
				pDisco->pendings--;	
				
			}
			else
			{
				/*if(pvez){
					discoSinc=(disco*)BuscarDiscoSinc();
					socketSincro=discoSinc->iSocketHDAParaDA;
					printf("socketSincro %i \n",socketSincro);
					pvez=0;
				}
				
				sectoresRecibidos++;
				pDisco->pendings--;
				printf("sectoresRecibidos %i \n",sectoresRecibidos);
				
				if(((sectoresRecibidos % 100)== 0)&&(sectoresEnviados<sectoresTotales)){
					contador=0;
					while((contador<100)&&(sectoresEnviados<sectoresTotales)){
						sectoresEnviados++;
						asignarread(pDisco,sectoresEnviados,0);
						contador++;						
					}
				}
				
				
				if((sectoresRecibidos<=sectoresTotales)){
				
					InfoIPC rInfoIPC33;
					rInfoIPC33.payLoadLength=rInfoIPC.payLoadLength;
					generarStructIPC(payload,WRITE_SECTORSYNC_REQUEST,&rInfoIPC33);
					enviarIPC(socketSincro,&rInfoIPC33);	
					free(rInfoIPC33.payload);
				}

				free(payload);
				*/
				
				if((VectorSincronizacion[atoi(buffer_id)].closeDiscoSinc!=1)&&(VectorSincronizacion[atoi(buffer_id)].closeDiscoLect!=1)){
				
					VectorSincronizacion[atoi(buffer_id)].sectoresRecibidos++;
					VectorSincronizacion[atoi(buffer_id)].discoLect->pendings--;
				
					if(((VectorSincronizacion[atoi(buffer_id)].sectoresRecibidos % 100)== 0)&&(VectorSincronizacion[atoi(buffer_id)].sectoresEnviados<sectoresTotales)){
						contador=0;
						while((contador<100)&&(VectorSincronizacion[atoi(buffer_id)].sectoresEnviados<sectoresTotales)){
							VectorSincronizacion[atoi(buffer_id)].sectoresEnviados++;
							asignarread(VectorSincronizacion[atoi(buffer_id)].discoLect,VectorSincronizacion[atoi(buffer_id)].sectoresEnviados,atoi(buffer_id));
							contador++;						
						}
					}
				
					if((VectorSincronizacion[atoi(buffer_id)].sectoresRecibidos<=sectoresTotales)){
				
						InfoIPC rInfoIPC33;
						rInfoIPC33.payLoadLength=rInfoIPC.payLoadLength;
						generarStructIPC(payload,WRITE_SECTORSYNC_REQUEST,&rInfoIPC33);
						enviarIPC(VectorSincronizacion[atoi(buffer_id)].discoSinc->iSocketHDAParaDA,&rInfoIPC33);	
						free(rInfoIPC33.payload);
					}
					else
						eliminar(atoi(buffer_id));
			
					free(payload);
				
				}
				else
				{
					
					if(VectorSincronizacion[atoi(buffer_id)].closeDiscoLect!=1){
						VectorSincronizacion[atoi(buffer_id)].discoLect->Estado==LISTO;
						eliminar(atoi(buffer_id));
					}
				}	
				
			}
		}
		
	}
}

void reallocateoperations(disco *dsk) {
	
	int ret;
	int flag=0;
	int noDisco=0;
	
	
	SacarDeLista_DA(&dsk);
	
	while((flag!=1)&&(noDisco!=-1)){
		ret=pthread_mutex_trylock(&mutex);	
		if (ret==0)
		{
			pedido* lib=(pedido *)BuscarPorDisco(colaGlobal,dsk->sNombre);
			if(lib!=NULL){
				if((lib->operacion==0)){
					lib->estado=0;
					noDisco=asignarPedido(&lib);
				}
			}
			else
				flag=1;
			
			if(pthread_mutex_unlock(&mutex)!=0){
				perror("pthread_mutex_lock");
				exit(EXIT_FAILURE);
			}
		}
	}
	
	
	if(noDisco==-1){
	
		
		if(estaVacia2(ListaDiscos)){
		
			ret=pthread_mutex_trylock(&mutex);	
			if (ret==0)
			{
			
				while(!estaVacia(colaGlobal)){
				
					pedido *aux=colaGlobal;
					colaGlobal=colaGlobal->Sgt;
					free(aux);
				}	
				
				
				if(pthread_mutex_unlock(&mutex)!=0){
						perror("pthread_mutex_lock");
						exit(EXIT_FAILURE);
				}
				
				return;
				
			}
		}
		
		else{
		
			flag=0;
			
			while((flag!=1)){
				ret=pthread_mutex_trylock(&mutex);	
				if (ret==0)
				{
					pedido* lib=(pedido *)BuscarPorDisco(colaGlobal,dsk->sNombre);
					if(lib!=NULL){
						if((lib->operacion==0)){
							SacarNodoDescriptoresDeLista2(&lib,&(colaGlobal),&(colaGlobal_Ult));
							free(lib);
						}
					}
					else
						flag=1;
			
					if(pthread_mutex_unlock(&mutex)!=0){
						perror("pthread_mutex_lock");
						exit(EXIT_FAILURE);
					}
					
				}	
			}
	
		}
	}
		
	free(dsk);
	
	iCantidadDeDAConectados--;
	if(estaVacia2(ListaDiscos)){
		Estado =FUERA_DE_SERVICIO;
		SubEstado = INCOMPLETO;
		printf("Se desconecta RAID porque no hay discos conectados \n");
		exit(EXIT_FAILURE);
	}
	
	printf("\nEstado del Array:\r\n");
	switch(Estado)
	{
		case INICIALIZADO:
			printf("INICIALIZADO\r\n");
		break;

		case FUERA_DE_SERVICIO:
			printf("FUERA DE SERVICIO\r\n");
		break;
	}
	switch(SubEstado)
	{
		case SINCRONIZADO:
			printf("SINCRONIZADO\r\n");
		break;

		case INCOMPLETO:
			printf("INCOMPLETO\r\n");
		break;

		case SINCRONIZANDO:
			printf("SINCRONIZANDO\r\n");
		break;
	}
	
	printf("DISCOS CONECTADOS:\n");
	printf("------------------\n");
	disco* pAuxDisco = ListaDiscos;
	if(pAuxDisco == NULL)
		printf("No hay\n");
	while(pAuxDisco != NULL)
	{
	  printf("%s\n", pAuxDisco->sNombre );
	  pAuxDisco = pAuxDisco->Sgt;	
	} 
			
}




int VerificarExistenciaDiscoSincronizados()
{
    struct DiscoConocido * pAuxDisco;
    int icantidad = 0;
	InfoIPC *infoIPC;
	
    pAuxDisco = ListaDiscos;
    while(pAuxDisco != NULL)
    {
	if ((pAuxDisco->Estado == LISTO)  || (pAuxDisco->Estado == SINCRONIZANDO_LEYENDO))
	    icantidad ++;
	pAuxDisco = pAuxDisco->Sgt;
    }

    return icantidad;
}


void intacad ( int n , char* cadena)
{	
    int j=0;
    int i=0;
	char aux;

	do
    {
		cadena[i] = (n % 10) + '0';    
		n=n/10;
		i++;
	}while (n > 0);

    cadena[i] = '\0';
    i--;

	while(i>j)
	{
		aux=cadena[i];
		cadena[i]=cadena[j];
		cadena[j]=aux;
		i--;
		j++;
	}

}

void* asignarread(disco* dsk,int sector, int pos) {
    
	char * sContador=(char*)malloc(20);
	int payload;
	char *sAux=(char *)malloc(20);
	int ret;
	int tamanio_buff ;
	
	/*strcpy(sContador,"-1");*/
	intacad(pos,sContador);
	dsk->pendings++;
	memset(sAux,'\0',20);
	intacad(sector,sAux);	
	payload=READ_SECTORSYNC_REQUEST;
	tamanio_buff = strlen(sContador) + 1 + strlen(sAux) + 2 ;
	char* buffer = (char *) malloc (sizeof(char) * tamanio_buff);
	strcpy(buffer, sContador);
	strcat(buffer, ",");
	strcat(buffer, sAux);
	strcat(buffer, ";");
	
	InfoIPC rInfoIPC3;
	rInfoIPC3.payLoadLength=tamanio_buff;
	generarStructIPC(buffer,payload,&rInfoIPC3);
	enviarIPC(dsk->iSocketHDAParaDA,&rInfoIPC3);	
	
	free(buffer);
	free(sAux);
	free(sContador);
	free(rInfoIPC3.payload);	

}

int enviarPendientes(char* nombre){
	
	int payload;
	char *sAux=(char *)malloc(20);
	int tamanio_buff;
	
	
	disco *dsk=BuscarDiscoPorNombre(nombre);
	
	if(!estaVacia(colaEspera)){
	

		while(!estaVacia(colaEspera)){

			if(pthread_mutex_lock(&mutex2)!=0){
				perror("pthread_mutex_lock");
				exit(EXIT_FAILURE);
			}
			
			
			pedido* nodo=SacarNodoDescriptoresDeLista(&(colaEspera),&(colaEspera_Ult));
			
			if(pthread_mutex_unlock(&mutex2)!=0){
				perror("pthread_mutex_lock");
				exit(EXIT_FAILURE);
			}
			
			
			strcpy(nodo->discoAsignado,"");
			memset(sAux,'\0',20);
			intacad(nodo->numeroSector,sAux);	
			payload=WRITE_SECTOR_REQUEST;
			dsk->pendings++;
			tamanio_buff = strlen(nodo->sDescriptorID_delPedido) + 1 + strlen(sAux) + 1 + 512 + 1;
			char* buffer = (char *) malloc (sizeof(char) * tamanio_buff);
			strcpy(buffer, nodo->sDescriptorID_delPedido);
			strcat(buffer, ",");
			strcat(buffer, sAux);
			strcat(buffer, ";");
			memcpy(buffer + strlen(nodo->sDescriptorID_delPedido) + 1 + strlen(sAux) + 1, nodo->data, 512);
			InfoIPC rInfoIPC3;
			rInfoIPC3.payLoadLength=tamanio_buff;
			generarStructIPC(buffer,payload,&rInfoIPC3);
			enviarIPC(dsk->iSocketHDAParaDA,&rInfoIPC3);
			free(rInfoIPC3.payload);
			nodo->estado = 1;
			printf("Se asigno escritura del sector %i al disco: %s \n",nodo->numeroSector,dsk->sNombre);
			free(nodo);
			free(buffer);
			
		}
	}
	
	
	/*Todos los discos pasan a estado listo*/
	disco* pAuxDisco = ListaDiscos;
    while(pAuxDisco != NULL)
    {
		pAuxDisco->Estado=LISTO;
		pAuxDisco = pAuxDisco->Sgt;
    }
	SubEstado = SINCRONIZADO;
	free(sAux);
}